package repository

import (
	"context"
	"go.uber.org/zap"
	"jk-time/webook/interactive/cache"
	"jk-time/webook/interactive/dao"
	"jk-time/webook/interactive/domain"
)

type InteractiveRepository interface {
	IncrReadCnt(ctx context.Context,
		biz string, bizId int64) error
	IncrLike(ctx context.Context, biz string, bizId, uid int64) error
	DecrLike(ctx context.Context, biz string, bizId, uid int64) error
	AddCollectionItem(ctx context.Context, biz string, bizId, cid int64, uid int64) error
	Get(ctx context.Context, biz string, bizId int64, uid int64) (domain.Interactive, error)
	Liked(ctx context.Context, biz string, id int64, uid int64) (bool, error)
	Collected(ctx context.Context, biz string, id int64, uid int64) (bool, error)
	BatchIncrReadCnt(ctx context.Context, bizs []string, ids []int64) error
	GetByIds(ctx context.Context, biz string, ids []int64) ([]domain.Interactive, error)
}
type CacheInteractiveRepository struct {
	dao   dao.InteractiveDAO
	cache cache.InteractiveCache
}

func NewCacheInteractiveRepository(dao dao.InteractiveDAO, cache cache.InteractiveCache) InteractiveRepository {
	return &CacheInteractiveRepository{dao: dao, cache: cache}
}
func (i *CacheInteractiveRepository) IncrReadCnt(ctx context.Context, biz string, bizId int64) error {
	err := i.dao.IncrReadCnt(ctx, biz, bizId)
	if err != nil {
		return err
	}
	return i.cache.IncrReadCntIfPresent(ctx, biz, bizId)
}
func (i *CacheInteractiveRepository) BatchIncrReadCnt(ctx context.Context, bizs []string,
	ids []int64) error {
	err := i.dao.BatchIncrReadCnt(ctx, bizs, ids)
	if err != nil {
		return err
	}
	return i.cache.IncrReadCntsIfPresent(ctx, bizs, ids)
}
func (i *CacheInteractiveRepository) IncrLike(ctx context.Context, biz string, bizId, uid int64) error {
	err := i.dao.InsertLikeInfo(ctx, dao.UserLikeBiz{
		BizId: bizId,
		Biz:   biz,
		Uid:   uid,
	})
	if err != nil {
		return err
	}
	return i.cache.IncrLikeCntIfPresent(ctx, biz, bizId)
}

func (i *CacheInteractiveRepository) DecrLike(ctx context.Context, biz string, bizId, uid int64) error {
	err := i.dao.DeleteLikeInfo(ctx, biz, bizId, uid)
	if err != nil {
		return err
	}
	return i.cache.DecrLikeCntIfPresent(ctx, biz, bizId)
}

func (i *CacheInteractiveRepository) AddCollectionItem(ctx context.Context, biz string, bizId, cid int64, uid int64) error {
	err := i.dao.InsertCollectionBiz(ctx, dao.UserCollectionBiz{
		Cid:   cid,
		BizId: bizId,
		Biz:   biz,
		Uid:   uid,
	})
	if err != nil {
		return err
	}
	return i.cache.IncrCollectCntIfPresent(ctx, biz, bizId)
}

func (i *CacheInteractiveRepository) Get(ctx context.Context, biz string, bizId int64, uid int64) (domain.Interactive, error) {
	intr, err := i.cache.Get(ctx, biz, bizId)
	if err == nil {
		return intr, err
	}
	interative, err := i.dao.Get(ctx, biz, bizId)
	if err != nil {
		return intr, err
	}
	go func() {
		err := i.cache.Set(ctx, biz, bizId, intr)
		if err != nil {
			zap.L().Error("回写缓存失败：", zap.Error(err))
		}
	}()
	return i.toDomain(interative), err
}

func (i *CacheInteractiveRepository) Liked(ctx context.Context, biz string, id int64, uid int64) (bool, error) {
	_, err := i.dao.GetLikeInfo(ctx, biz, id, uid)
	switch err {
	case nil:
		return true, nil
	case dao.ErrRecordnotFound:
		//吞掉
		return false, nil
	default:
		return false, err
	}
}
func (i *CacheInteractiveRepository) GetByIds(ctx context.Context, biz string,
	ids []int64) ([]domain.Interactive, error) {
	intrs, err := i.dao.GetByIds(ctx, biz, ids)
	doIntrs := make([]domain.Interactive, 0)
	switch err {
	case nil:
		for _, intr := range intrs {
			doIntrs = append(doIntrs, i.toDomain(intr))
		}
		return doIntrs, nil
	case dao.ErrRecordnotFound:
		//吞掉
		return []domain.Interactive{}, nil
	default:
		return []domain.Interactive{}, err
	}
}
func (i *CacheInteractiveRepository) Collected(ctx context.Context, biz string, id int64, uid int64) (bool, error) {
	_, err := i.dao.GetLikeInfo(ctx, biz, id, uid)
	switch err {
	case nil:
		return true, nil
	case dao.ErrRecordnotFound:
		//吞掉
		return false, nil
	default:
		return false, err
	}
}

func (i *CacheInteractiveRepository) toDomain(intr dao.Interactive) domain.Interactive {
	return domain.Interactive{
		BizId:      intr.BizId,
		LikeCnt:    intr.LikeCnt,
		CollectCnt: intr.CollCnt,
		ReadCnt:    intr.ReadCnt,
	}
}
